<!--
 * @author luguoxiang
 * @date 2022/9/17
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
-->
<template>
  <div style="margin-bottom: 16px; display: flex; align-items: center">
    <t-button size="small" @click="all()">全选</t-button>
    <t-button size="small" @click="clear()">全不选</t-button>
    <t-input :value="keyword" style="margin-left: var(--td-comp-margin-s)" @change="onSearch" />
  </div>
  <div v-if="data.length" style="display: flex; height: calc(100vh - 180px)" class="menu-select">
    <div style="width: 250px; height: 100%; overflow: auto">
      <t-collapse :value="[activeSys.value]" expand-mutex @change="clickSystem">
        <t-collapse-panel v-for="x in data" :key="x.key" :value="x.value">
          <template #header>
            <t-checkbox :checked="x.checked" :indeterminate="x.indeterminate" @click.stop @change="change(x, $event)" />
            {{ x.label }}
          </template>
          <directory
            :data="x.ds"
            :click-check="false"
            :active="activeDir.value"
            @click-node="clickDir"
            @change="change"
          />
        </t-collapse-panel>
      </t-collapse>
    </div>
    <div style="flex: 1; padding-left: 16px; height: 100%; display: flex; flex-direction: column">
      <t-breadcrumb max-item-width="150" style="margin-bottom: 19px">
        <t-breadcrumbItem>{{ activeSys.label }}</t-breadcrumbItem>
        <t-breadcrumbItem v-for="x in breadcrumb" :key="x.key" @click="clickDir(x)">{{ x.label }}</t-breadcrumbItem>
      </t-breadcrumb>
      <lg-auto-size>
        <table class="gl-table">
          <tr class="gl-table-header">
            <th style="width: 180px">菜单</th>
            <th>权限</th>
          </tr>
          <template v-if="menuList.length">
            <tr v-for="x in menuList" :key="x.key">
              <td>
                <t-checkbox :checked="x.checked" :indeterminate="x.indeterminate" @change="change(x, $event)">
                  {{ x.label }}
                </t-checkbox>
              </td>
              <td>
                <div class="t-checkbox-group">
                  <t-checkbox
                    v-for="p in x.ps"
                    :key="p.key"
                    :checked="p.checked"
                    :indeterminate="p.indeterminate"
                    @change="change(p, $event)"
                  >
                    {{ p.label }}
                  </t-checkbox>
                </div>
              </td>
            </tr>
          </template>
          <tr v-else>
            <td colspan="2">
              <div class="gl-empty">暂无数据</div>
            </td>
          </tr>
        </table>
      </lg-auto-size>
    </div>
  </div>
  <div v-else class="gl-empty">暂无数据</div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue';
import Directory from './Directory.vue';
import menuApi from '../api';
import { deepCopy, forEach, LgAutoSize, nextId, query } from '@/soar';

const props = defineProps({
  modelValue: {
    type: Array,
    default() {
      return [];
    },
  },
});

const emits = defineEmits(['update:modelValue']);

function changeChecked(list: any[]) {
  for (const x of list) {
    if (x.children?.length) {
      changeChecked(x.children);
      let count = 0;
      for (const c of x.children) {
        if (c.indeterminate) {
          count = x.children.length + 1;
          break;
        }
        if (c.checked) {
          count++;
        }
      }

      if (count === 0) {
        x.indeterminate = false;
        x.checked = false;
      } else if (count === x.children.length) {
        x.indeterminate = false;
        x.checked = true;
      } else {
        x.indeterminate = true;
        x.checked = false;
      }
    } else {
      x.indeterminate = false;
      x.checked = props.modelValue?.includes(x.value);
    }
    x.key = nextId();
  }
}

const keyword = ref('');
function onSearch(e: string) {
  keyword.value = e;
  const tree = query(deepCopy(source), (x: any) => x.label.includes(e));
  handleTree(tree);
  changeChecked(tree);
  data.value = tree;
  menuList.value = [];
  if (tree.length) {
    clickSystem([tree[0]?.value]);
  }
}

function change(node: any, checked: boolean) {
  const selection = props.modelValue;
  if (checked) {
    forEach([node], (x) => {
      if (!selection.includes(x.value)) {
        selection.push(x.value);
      }
    });
  } else {
    forEach([node], (x) => {
      const index = selection.indexOf(x.value);
      if (index !== -1) {
        selection.splice(index, 1);
      }
    });
  }
  changeChecked(data.value);

  emits('update:modelValue', selection);
}

function all() {
  data.value.forEach((x: any) => {
    change(x, true);
  });
}

function clear() {
  data.value.forEach((x: any) => {
    change(x, false);
  });
}

const activeSys = ref({});
function clickSystem(e) {
  activeSys.value = data.value.find((x) => x.value === e[0]) ?? {};
  clickDir(activeSys.value.ds?.[0] ?? {});
}

const breadcrumb = ref([]);
const activeDir = ref({});
const menuList = ref([]);
function clickDir(dir) {
  dir.expanded = true;
  if (dir.ds?.length) {
    clickDir(dir.ds[0]);
    return;
  }
  activeDir.value = dir;
  menuList.value = dir.ms ?? [];
  breadcrumb.value = getParent(dir);
}

function getParent(dir) {
  if (dir.parent?.type === 2) {
    return getParent(dir.parent).concat(dir);
  }
  return [dir];
}

function handleTree(tree: any[], parent = null) {
  for (const x of tree) {
    x.key = nextId();
    x.parent = parent;
    if (!x.children?.length) {
      delete x.ds;
      delete x.ms;
      delete x.ps;
      continue;
    }
    handleTree(x.children, x);
    const ps = [];
    const ms = [];
    const ds = [];
    for (const item of x.children) {
      switch (item.type) {
        case 0:
          ps.push(item);
          break;
        case 1:
          ms.push(item);
          break;
        case 2:
          ds.push(item);
          break;
        default:
          break;
      }
    }
    const dsLength = ds.length;

    if (ps.length) {
      const menu = {
        type: 1,
        value: `0-${x.value}-0`,
        label: '默认菜单',
        children: ps,
        parent: x,
        ps,
      };
      for (const p of ps) {
        p.parent = menu;
      }
      ms.unshift(menu);
    }

    if (ms.length) {
      const dir = {
        type: 2,
        value: `0-${x.value}`,
        label: '默认目录',
        children: ms,
        parent: x,
        ms,
      };
      for (const m of ms) {
        m.parent = dir;
      }
      ds.unshift(dir);
    }

    switch (x.type) {
      case 3:
        x.ds = ds;
        x.children = ds;
        break;
      case 2:
        if (dsLength) {
          x.ds = ds;
          x.children = ds;
        } else {
          x.ms = ms;
          x.children = ms;
        }
        break;
      case 1:
        x.ps = ps;
        x.children = ps;
        break;
      default:
        break;
    }
  }
}

function toTreeData(list: any[], pid: string) {
  const res = [];
  for (const x of list) {
    if (x.pId === pid) {
      x.children = toTreeData(list, x.value);
      res.push(x);
    }
  }
  return res;
}

let source = [];
const data = ref([]);
menuApi.query({ $soarQuery: { fields: 'id,title,parentId,type', orderBys: 'sort' }, queryType: 'list', pageSize: 999999 }).then((res) => {
  // 数据转换
  res = res.list.map((x: { type: any; id: any; title: any; parentId: string }) => {
    return {
      type: x.type,
      value: x.id,
      label: x.title,
      pId: x.parentId,
      checked: false,
      indeterminate: false,
    };
  });
  let tree = toTreeData(res, '0');
  let systems = tree.filter((x) => x.type !== 3);
  systems = systems.filter((x) => x.type === 2);
  const defSys = {
    type: 3,
    value: '0',
    label: '默认系统',
    children: systems,
  };
  tree = tree.filter((x) => x.type === 3);
  tree.unshift(defSys);
  source = deepCopy(tree);

  handleTree(tree);
  changeChecked(tree);
  data.value = tree;
  clickSystem([tree[0]?.value]);
});

watch(
  () => props.modelValue,
  (v) => {
    changeChecked(data.value);
  },
  { immediate: true },
);
</script>

<style lang="less" scoped>
.menu-select {
  :deep(.t-collapse-panel__content) {
    padding-left: 12px;
  }
}
</style>
